package org.javaforever.infinity.compiler;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.javaforever.infinity.domain.DBDefinitionGenerator;
import org.javaforever.infinity.domain.Domain;
import org.javaforever.infinity.domain.Field;
import org.javaforever.infinity.domain.Prism;
import org.javaforever.infinity.domain.Project;
import org.javaforever.infinity.domain.ValidateInfo;
import org.javaforever.infinity.exception.ValidateException;
import org.javaforever.infinity.generator.MysqlDBDefinitionGenerator;

public class SGSCompiler {
 
	public SGSCompiler() {}
	public static Project translate(String sgs) {
		try {
			List<String> notions = parseNotions(sgs);
			Project project = notionsToProject(notions);	
			return project;
		} catch (ValidateException e){
			ValidateInfo info = e.getValidateInfo();
			for (String s: info.getCompileErrors()){
				System.out.println(s);
			}
			return null;
		}
	}
	
	public static List<String> parseNotions(String sgs){
		return SGSTokenizer.generateTokens(sgs);
	}
	
	public static Project notionsToProject(List<String> notions) throws ValidateException {
		Project project;
		Stack<String> projectStack = new Stack<String>();
		DBDefinitionGenerator dbdg = new MysqlDBDefinitionGenerator();
		for (int i=0; i < notions.size();i++) {
			if ("project".equals(notions.get(i))){
				project = new Project();
				if (!isKeyword(notions.get(i+1))){
					project.setStandardName(notions.get(i+1));
					project.setDbName(project.getStandardName());
					dbdg.setDbName(notions.get(i+1));
					if ("{".equals(notions.get(i+2))){
						projectStack.push("{");
						for (int j=i+3; j < notions.size();j++) {

						if ("packagetoken".equals(notions.get(j))){
							if (":".equals(notions.get(j+1))&&!isKeyword(notions.get(j+2))) project.setPackageToken(notions.get(j+2));
							if (";".equals(notions.get(j+3))) j += 4;
						}
						
						if ("dbprefix".equals(notions.get(j))){
							if (":".equals(notions.get(j+1))&&!isKeyword(notions.get(j+2))) project.setDbPrefix(notions.get(j+2));
							if (";".equals(notions.get(j+3))) j += 4;
						}
						
						List<Domain> domainList = parseDomains(notions.subList(j, notions.size()),projectStack,project.getPackageToken());
						project.setDomains(domainList);
						dbdg.setDomains(domainList);
						project.addDBDefinitionGenerator(dbdg);
						List<Prism> prismList = parsePrisms(notions.subList(j, notions.size()),projectStack,domainList,project.getPackageToken());
						project.setPrisms(prismList);
						return project;
						}
					}			
				}			
			}
		}
		return null;
	}
	
	public static List<Prism> parsePrisms(List<String> notions, Stack<String> projectStack, List<Domain> domainList, String packageToken) throws ValidateException{
		List<Prism> list = new ArrayList<Prism>();
		Prism prism = new Prism();
		boolean prismStackjOverflow = false;
		Stack<String> prismStack = new Stack<String>();
		boolean started = false;
		int totalPrismCounts = countPrisms(notions);
		
		for (int i=0; i < notions.size();i++) {
			if  (!"prism".equals(notions.get(i))) {
				notions.remove(i);
				i = i - 1;
			} else {
				break;
			}
		}
		
		for (int i = 0; i < notions.size(); i++){

			if ("prism".equals(notions.get(i))){
				prism = new Prism();
				if (!isKeyword(notions.get(i+1))){
					prism.setStandardName(notions.get(i+1));
				}
				if ("{".equals(notions.get(i+2))){
					prismStack.push("{");
					projectStack.push("{");
				}
				i= i +3;
			}
			
			if ("{".equals(notions.get(i))){
				if (!prismStackjOverflow){
					prismStack.push("{");
				}else{
					prismStackjOverflow = false;
				}
				projectStack.push("{");
			}
			
			if ("}".equals(notions.get(i))){
				if (!prismStack.empty()){
					prismStack.pop();
				} else {
					prismStackjOverflow = true;
				}
				projectStack.pop();
			}
					
			if (prismStack.empty()&&list.size() < totalPrismCounts){
				list.add(prism);
			}else if (prismStack.empty()&&list.size()== totalPrismCounts){
				return list;
			}
			
			if ("prismdomain".equals(notions.get(i))){
				if (":".equals(notions.get(i+1))){
					if (!isKeyword(notions.get(i+2))){
						Domain d = findDomainFromListByStandardName(domainList, notions.get(i+2));
						if (d!=null) prism.setDomain(d);
						prism.setPackageToken(packageToken);
						prism.generatePrismFromDomain();
						prism.expandPackageToken();
						i = i+2;
						started = true;
					}
				}
			 }	
		 }				
		return null;
	}
	
	public static  Domain findDomainFromListByStandardName(List<Domain> domainList, String standardName){
		for (Domain d:domainList){
			if (d.getStandardName().equals(standardName)) return d;
		}
		return null;
	}
	
	public static List<Domain> parseDomains(List<String> notions, Stack<String> projectStack, String packageToken){
		List<Domain> list = new ArrayList<Domain>();
		Domain domain = new Domain();
		boolean domainStackjOverflow = false;
		Stack<String> domainStack = new Stack<String>();
		int totalDomainCounts = countDomains(notions);
		
		for (int i = 0; i < notions.size(); i++){			
			if ("domain".equals(notions.get(i))){
				domain = new Domain();
				domain.setPackageToken(packageToken);
				if (!isKeyword(notions.get(i+1))){
					domain.setStandardName(notions.get(i+1));					
				}
				if ("{".equals(notions.get(i+2))){
					domainStack.push("{");
					projectStack.push("{");
				}
				i= i +3;
				//continue;
			}
			
			if ("{".equals(notions.get(i))){
				if (!domainStackjOverflow){
					domainStack.push("{");
				}else{
					domainStackjOverflow = false;
				}
				projectStack.push("{");
				//continue;
			}
			
			if ("}".equals(notions.get(i))){
				if (!domainStack.empty()){
					domainStack.pop();
				} else {
					domainStackjOverflow = true;
				}
				projectStack.pop();
				//continue;
				if (domainStack.empty() && domainStackjOverflow==false && list.size() < totalDomainCounts){
					list.add(domain);
				}else if (domainStack.empty()&&list.size()== totalDomainCounts){
					return list;
				}				
			}			
			
			if ("domainid".equals(notions.get(i))){
				if (notions.get(i+1).equals(":")&& !isKeyword(notions.get(i+2)) && !isKeyword(notions.get(i+3))){
					Field f = new Field();
					f.setFieldName(notions.get(i+2));
					f.setFieldType("long");					
					domain.setDomainId(f);
					i += 3;
					continue;
				}
		 }	
			
			if ("domainname".equals(notions.get(i))){
				if (notions.get(i+1).equals(":")&& !isKeyword(notions.get(i+2)) && !isKeyword(notions.get(i+3))){
					Field f = new Field();
					f.setFieldName(notions.get(i+2));
					f.setFieldType("String");					
					domain.setDomainName(f);
					i += 3;
					continue;
				}
		 }
		
			if ("activefield".equals(notions.get(i))){
				if (notions.get(i+1).equals(":")&& !isKeyword(notions.get(i+2)) && !isKeyword(notions.get(i+3))){
					Field f = new Field();
					f.setFieldName(notions.get(i+2));
					f.setFieldType("boolean");					
					domain.setActive(f);
					i += 3;
					continue;
				}
		 }
			
			if ("plural".equals(notions.get(i))){
				if (notions.get(i+1).equals(":")&& !isKeyword(notions.get(i+2)) && !isKeyword(notions.get(i+3))){
					String plural = notions.get(i+2);
					domain.setPlural(plural);
					i += 3;
					continue;
				}
			}
			
			if ("field".equals(notions.get(i))){
				if (notions.get(i+1).equals(":")&& !isKeyword(notions.get(i+2)) && !isKeyword(notions.get(i+3)) && ";".equals(notions.get(i+4))){
					Field f = new Field(notions.get(i+2),notions.get(i+3));
					domain.addField(f);	
					i += 3;
				}else if (!isKeyword(notions.get(i+4)) && !";".equals(notions.get(i+4))&&!"}".equals(notions.get(i+4))){
					Field f = new Field(notions.get(i+2),notions.get(i+3),notions.get(i+4));
					domain.addField(f);	
					i += 4;
				}						
				continue;
			}
			

			if (";".equals(notions.get(i))) continue;
		}				
		return null;
	}
	
	public static boolean isKeyword(String notion){
		if ("project".equals(notion)||"prism".equals(notion)||"domain".equals(notion)||"field".equals(notion)
				||"packagetoken".equals(notion)||"plural".equals(notion)||"activefield".equals(notion)
				||"domainname".equals(notion)||"domainid".equals(notion)||"prismdomain".equals(notion)) return true;
		else return false;
	}
	
	public static int countDomains(List<String> notions){
		int count = 0;
		for (String s:notions){
			if (s.equals("domain")) count ++;
		}
		return count;
	}
	
	public static int countPrisms(List<String> notions){
		int count = 0;
		for (String s:notions){
			if (s.equals("prism")) count ++;
		}
		return count;
	}
}
